<template>
  <div class="form-container">
    <a-spin :spinning="spinning" :tip="spinningMessage">
      <a-form :form="form">
        <!-- <a-form :form="form" :label-col="{span:9}" :wrapper-col="{span:9}"> -->
        <a-collapse v-model="collapseActiveKey">
          <!-- 基本信息设置 -->
          <a-collapse-panel key="1" header="基本信息">
            <!-- 导入数据的类型 -->
            <div class="import-row-first">
              <a-form-item
                label="数据类型"
                :label-col="{ span: 3 }"
                :label-align="'left'"
              >
                <!-- 选择数据类型 新用户引导 step-3 -->
                <guide-step-popover :guide-step="3">
                  <template #header>
                    <a-button-group
                      v-decorator="[
                        'dataType',
                        {
                          rules: [
                            {
                              required: categoryRequired,
                              message: '请选择数据类型!',
                            },
                          ],
                          initialValue: 'table',
                        },
                      ]"
                      class="import-data-type"
                    >
                      <a-button
                        value="table"
                        :class="
                          'table' === selectedDataType
                            ? 'active-type-button'
                            : 'primary-type-button'
                        "
                        @click="changeDataType('table')"
                      >
                        表格文件
                      </a-button>
                      <a-button
                        value="database"
                        :class="
                          'database' === selectedDataType
                            ? 'active-type-button'
                            : 'primary-type-button'
                        "
                        @click="changeDataType('database')"
                      >
                        数据库
                      </a-button>
                      <a-button
                        value="graph"
                        :class="
                          'graph' === selectedDataType
                            ? 'active-type-button'
                            : 'primary-type-button'
                        "
                        @click="changeDataType('graph')"
                      >
                        图文件
                      </a-button>
                      <a-button
                        value="http"
                        :class="
                          'http' === selectedDataType
                            ? 'active-type-button'
                            : 'primary-type-button'
                        "
                        @click="changeDataType('http')"
                      >
                        http数据
                      </a-button>
                      <!-- Gis 数据  -->
                      <a-button
                        value="http"
                        :class="
                          'gis' === selectedDataType
                            ? 'active-type-button'
                            : 'primary-type-button'
                        "
                        @click="changeDataType('gis')"
                      >
                        GIS文件
                      </a-button>
                    </a-button-group>
                  </template>
                </guide-step-popover>

                <!-- 说明 -->
                <div
                  v-if="
                    selectedDataType === 'table' ||
                    selectedDataType === 'http' ||
                    selectedDataType === 'gis'
                  "
                  class="import-data-type-prompt"
                >
                  <div v-if="selectedDataType === 'table'">
                    <strong>说明</strong>：1、支持表格文件格式：csv、excel
                  </div>
                  <div v-if="selectedDataType === 'http'">
                    <strong>说明</strong>：1、支持表格文件格式：txt、json
                  </div>
                  <div v-if="selectedDataType === 'gis'">
                    <strong>说明</strong
                    >：1、支持表格文件格式：excel、csv、shapefile(至少包括.dbf、.shp、.shx
                    文件，如果没有上传.prj文件，则使用默认坐标系WGS_1984)
                  </div>
                  <div style="margin-left: 39px;">
                    2、文件大小限制：{{ fileSizeLimit[selectedDataType] }}
                  </div>
                  <div
                    v-if="selectedDataType === 'gis'"
                    style="margin-left: 39px;"
                  >
                    3、数据表格类型文件：若为点类型，要求有经度、纬度为独立的两列数据；
                    若为面数据，要求有一列为行政区名
                  </div>
                  <div
                    v-if="selectedDataType === 'table'"
                    style="margin-left: 39px;"
                  >
                    3、不支持多级标题的表格文件，不支持处理超过10个sheet的excel文件
                  </div>
                  <div
                    v-if="selectedDataType === 'table'"
                    style="margin-left: 39px;"
                  >
                    4、默认编码：utf-8。若上传完成后预览数据为空，请选择正确的文件编码。
                  </div>
                </div>
                <!-- 图数据 导入说明 -->
                <div
                  v-if="selectedDataType === 'graph'"
                  class="import-data-type-prompt"
                  :style="{
                    height: isNoteUnfold ? '515px' : '70px',
                    overflow: 'hidden',
                  }"
                >
                  <div><strong>说明</strong>：1、支持文件格式：json</div>
                  <div style="margin-left: 39px;">2、文件大小限制：2GB</div>
                  <div style="margin-left: 39px;">
                    3、数据格式：
                    <pre>
{
    "nodes": [
      {
        "id": 0,
        "categoryId": 1,
        *"label": "node0",
        *"attributes": {},
        *"config": {}
      },
      {
        "id": 1,
        "categoryId": 2,
        *"label": "node1",
        *"attributes": {},
        *"config": {}
      }
    ],
    "links": [
      {
        "id": 0,
        "source": 0,
        "target": 1,
        *"label": "link0",
        *"directed": false,
        *"weight": 1,
        *"attributes": {},
        *"config": {}
      }
    ]
  }
  </pre
                    >
                    （*为可选字段；id必须为整数，包括边的source和target；未设边的directed属性时默认为无向）
                  </div>
                </div>
                <div
                  v-if="selectedDataType === 'graph'"
                  class="import-data-note-fold"
                  @click="changeNoteState"
                >
                  {{ isNoteUnfold ? '收起' : '展开' }}
                  <a-icon
                    class="up-down"
                    :type="isNoteUnfold ? 'up' : 'down'"
                  />
                </div>
              </a-form-item>
            </div>

            <!-- 导入文件(表格文件) -->
            <div
              v-if="selectedDataType === 'table'"
              class="import-row-two-file"
            >
              <div class="upload-file-select">
                <a-form-item
                  label="导入文件"
                  :label-col="{ span: 8 }"
                  :label-align="'left'"
                >
                  <a-upload
                    v-decorator="[
                      'file',
                      {
                        rules: [
                          {
                            validator: (rule, value, callback) => {
                              if (fileList.length === 0) {
                                callback('请上传文件!')
                                return false
                              }
                              return true
                            },
                          },
                        ],
                      },
                    ]"
                    :custom-request="uploadDataset"
                    accept=".csv,.xlsx,.xls"
                    :file-list="fileList"
                    class="csv-upload"
                    :before-upload="beforeUpload"
                    @change="handleUpload"
                  >
                    <a-button
                      style="width: 200px;"
                      @mouseover="setHover('select-file')"
                      @mouseout="setHover('')"
                    >
                      <img
                        v-if="hover === 'select-file'"
                        alt=""
                        src="@/assets/form/select-file-active.svg"
                      />
                      <img v-else alt="" src="@/assets/form/select-file.svg" />
                      <span style="margin-left: 7px;">选择文件</span>
                    </a-button>
                  </a-upload>
                </a-form-item>
              </div>
              <div v-if="fileList.length !== 0">
                <div
                  class="upload-file-info"
                  :style="{ height: '25px', lineHeight: '40px' }"
                >
                  <a-icon-font type="iconxuanzewenjian-copy" />
                  <div class="upload-file-text">{{ fileList[0].name }}</div>
                  <img
                    alt=""
                    src="@/assets/form/delete-file.svg"
                    @click="deleteFile()"
                  />
                </div>
                <div class="progress-loading">
                  <a-progress
                    style="flex: 1;"
                    :percent="loadingProgress"
                    :status="uploadingState"
                    :show-info="true"
                  />
                  <div
                    class="loading-text"
                    :style="{
                      color:
                        uploadingState === 'complete' ? '#53c41c' : '#5c5c5c',
                    }"
                  >
                    {{ loadingText }}
                  </div>
                </div>
              </div>
            </div>

            <!-- 外部数据库 (数据库) -->
            <div
              v-if="selectedDataType === 'database'"
              class="import-row-two-database"
            >
              <div class="database-type">
                <a-form-item
                  label="数据库类型"
                  :label-col="{ span: 8 }"
                  :help="databaseConfigValidateMessage"
                  :label-align="'left'"
                >
                  <a-radio-group
                    v-model="selectedDatabaseType"
                    v-decorator="[
                      'databaseType',
                      {
                        rules: [
                          {
                            required: selectedDataType === 'database',
                            message: '请选择数据库类型!',
                          },
                        ],
                        initialValue: 'mysql',
                      },
                    ]"
                    @change="changeDatabaseType"
                  >
                    <a-radio-button
                      v-for="element in databaseButtonImg"
                      :key="element['name']"
                      class="select-box"
                      :value="element['name']"
                    >
                      <img
                        :src="element.logo"
                        :alt="element['name']"
                        style="height: 50px; width: 78px;"
                      />
                    </a-radio-button>
                  </a-radio-group>
                </a-form-item>
              </div>
            </div>

            <!-- 数据库导入模型 (数据库) 不是模态框-->
            <database-modal
              v-if="selectedDataType === 'database'"
              :selected-database-type="selectedDatabaseType"
              @set-is-database-chosen="setIsDatabaseChosen"
            />

            <!--  外部数据库 选择数据的表 (数据库)  -->
            <div
              v-if="selectedDataType === 'database'"
              class="select-table-column"
              :style="{
                'margin-bottom':
                  (isDatabaseChosen &&
                    databaseTables &&
                    databaseTables.length > 0) ||
                  (isDatabaseChosen && databaseTables.length === 0)
                    ? '6px'
                    : '0px',
              }"
            >
              <div
                v-if="
                  isDatabaseChosen &&
                  databaseTables &&
                  databaseTables.length > 0
                "
                class="database-select-import"
              >
                <a-form-item
                  label="选择导入数据"
                  :label-align="'left'"
                  :label-col="{ span: 5 }"
                  :wrapper-col="{ span: 19 }"
                >
                  <a-select
                    v-decorator="[
                      'tables',
                      {
                        rules: [
                          { required: true, message: '请选择导入的数据表!' },
                        ],
                      },
                    ]"
                    mode="multiple"
                    placeholder="请选择"
                    size="small"
                    class="col-container"
                    @change="changeSelectTables"
                  >
                    <a-select-option
                      v-for="table in databaseTables"
                      :key="table"
                      :value="table"
                      >{{ table }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </div>
              <div
                v-else-if="isDatabaseChosen && databaseTables.length === 0"
                class="no-table-text"
              >
                数据库 {{ currentUploadDatabaseName }} 没有数据表
              </div>
            </div>

            <!-- 图数据导入（图文件） -->
            <div
              v-if="selectedDataType === 'graph'"
              class="import-row-two-file"
            >
              <div class="upload-file-select">
                <a-form-item
                  label="导入文件"
                  :label-col="{ span: 8 }"
                  :label-align="'left'"
                >
                  <a-upload
                    v-decorator="[
                      'file',
                      {
                        rules: [
                          {
                            validator: (rule, value, callback) => {
                              if (fileList.length === 0) {
                                callback('请上传文件!')
                                return false
                              }
                              return true
                            },
                          },
                        ],
                      },
                    ]"
                    :custom-request="uploadDataset"
                    accept=".csv,.json,.gml"
                    :file-list="fileList"
                    class="csv-upload"
                    @change="handleUpload"
                  >
                    <a-button
                      style="width: 200px;"
                      @mouseover="setHover('select-file')"
                      @mouseout="setHover('')"
                    >
                      <img
                        v-if="hover === 'select-file'"
                        alt=""
                        src="@/assets/form/select-file-active.svg"
                      />
                      <img v-else alt="" src="@/assets/form/select-file.svg" />
                      <span style="margin-left: 7px;">选择文件</span>
                    </a-button>
                  </a-upload>
                </a-form-item>
              </div>
              <div v-if="fileList.length !== 0" class="upload-file-info">
                <a-icon-font type="iconxuanzewenjian-copy" />
                <div class="upload-file-text">{{ fileList[0].name }}</div>
                <img
                  alt=""
                  src="@/assets/form/delete-file.svg"
                  @click="deleteFile()"
                />
              </div>
            </div>

            <!-- http接入 (http数据) -->
            <div v-if="selectedDataType === 'http'" class="select-http-column">
              <div class="data-type-box">
                <a-form-item
                  label="数据类型"
                  label-align="left"
                  :label-col="{ span: 8 }"
                  :wrapper-col="{ span: 14 }"
                >
                  <a-radio-group
                    v-decorator="[
                      'importType',
                      {
                        rules: [{ required: true, message: '请选择数据类型' }],
                        initialValue: 'json',
                      },
                    ]"
                    @change="dataTypeChanged"
                  >
                    <a-radio-button key="json" value="json" class="select-box">
                      <img
                        :src="httpButtonImg"
                        alt="json"
                        style="height: 50px; width: 78px;"
                      />
                    </a-radio-button>
                  </a-radio-group>
                  <!-- <a-select
                    v-decorator="[
                      'importType',
                      {
                        rules: [{ required: true, message: '请选择数据类型' }],
                        initialValue: 'json',
                      },
                    ]"
                    placeholder="请选择"
                    size="small"
                    @change="dataTypeChanged"
                  >
                    <a-select-option key="json" value="json">json</a-select-option>
                  </a-select> -->
                </a-form-item>
              </div>
              <!-- <a-button
                class="http-setting-button"
                type="link"
                @click="onHttpSettingBtnClick"
              >
                <a-icon-font class="setting-icon" type="iconbianjizhi" />
                <span v-if="isDatabaseChosen">编辑配置</span>
                <span v-else>配置</span>
              </a-button> -->
            </div>

            <!-- 选择编码 (表格文件) -->
            <div
              v-if="selectedDataType === 'table'"
              class="import-row-general import-row-general-required"
            >
              <!-- 选择编码 -->
              <div class="row-three-column">
                <a-form-item
                  label="文件编码"
                  :label-col="{ span: 8 }"
                  :label-align="'left'"
                >
                  <a-select
                    v-decorator="[
                      'codingType',
                      {
                        rules: [{ required: true, message: '请选择编码!' }],
                        initialValue: datasetCodingType[0],
                      },
                    ]"
                    placeholder="请选择"
                    class="select-box"
                    size="small"
                    style="width: 316px;"
                    :disabled="isUploading"
                    @change="codingTypeChanged"
                  >
                    <a-select-option
                      v-for="codingType in datasetCodingType"
                      :key="codingType"
                      :value="codingType"
                      >{{ codingType }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </div>
            </div>

            <!-- 选择分类文件夹 （图文件 除外） -->
            <div
              v-if="selectedDataType !== 'graph' && selectedDataType !== 'gis'"
              class="import-row-general import-row-general-required"
            >
              <!-- 导入数据分类 -->
              <div class="row-three-column">
                <a-form-item
                  label="选择文件夹"
                  :label-col="{ span: 8 }"
                  :label-align="'left'"
                >
                  <a-select
                    v-decorator="[
                      'category',
                      {
                        rules: [
                          { required: true, message: '请选择数据文件夹!' },
                        ],
                        initialValue: selectedCategory,
                      },
                    ]"
                    placeholder="请选择"
                    class="select-box"
                    size="small"
                    style="width: 316px;"
                  >
                    <a-select-option
                      v-for="category in categorySelectOptions"
                      :key="category"
                      :value="category"
                      >{{ category }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </div>
            </div>

            <!-- http配置 -->
            <HttpModal
              v-if="selectedDataType === 'http'"
              @onok="onHttpModalSubmit"
            ></HttpModal>

            <!-- 数据集名称 （图文件 除外） -->
            <div
              v-if="selectedDataType !== 'graph' && selectedDataType !== 'gis'"
              class="import-row-general import-row-general-required"
            >
              <!-- 导入文件模式 数据集名称 -->
              <div
                v-if="selectedDataType === 'table'"
                ref="datasetName"
                class="row-three-column"
                style="width: 440px;"
              >
                <a-form-item
                  label="数据集名称"
                  :label-col="{ span: 9 }"
                  :label-align="'left'"
                >
                  <a-input
                    v-decorator="[
                      'datasetName',
                      {
                        rules: [
                          { required: true, message: '请输入数据集名称!' },
                          {
                            validator: (rule, value, callback) => {
                              if (value.includes(' ')) {
                                callback('数据表名不允许含有空格!')
                                return false
                              }
                              return true
                            },
                          },
                        ],
                        validateTrigger: ['blur', 'change'],
                        initialValue: datasetName,
                      },
                    ]"
                    class="name-input"
                    size="small"
                    style="width: 316px;"
                    :max-length="50"
                    @change="changeDatabaseName"
                  >
                    <a-icon
                      v-show="datasetNameClearStatus"
                      slot="suffix"
                      type="close-circle"
                      @mousedown="clearDatasetName"
                    />
                  </a-input>
                  <br />
                </a-form-item>
              </div>
              <!-- 将第一行作为数据表的列字段名称 -->
              <div
                v-if="selectedDataType === 'table'"
                class="set-first-row row-three-column"
                style="width: 440px;"
              >
                <a-checkbox
                  :checked="firstLineAsFields"
                  style="margin-right: 0;"
                  @click="changeFirstLine"
                >
                  将第一行作为数据表的列字段名称
                </a-checkbox>
              </div>
            </div>

            <div class="upload-tips">
              提示：如更改表头信息，请点击数据表头名称进行编辑
            </div>

            <!-- gis 文件数据导入 单独封装  -->
            <gis-modal
              v-if="selectedDataType === 'gis'"
              ref="gisModalCom"
              @confirm-visible="gisModalEmit"
              @update-spin-state="updateSpinState"
              @gis-data-change="handleGisDataChange"
            />
          </a-collapse-panel>

          <!-- 高级配置 -->
          <a-collapse-panel
            v-if="selectedDataType === 'table' && fileType === 'csv'"
            key="2"
            header="高级设置"
          >
            <div class="import-row-general import-row-general-norequired">
              <!-- 分隔符号 -->
              <div class="row-three-column">
                <a-form-item
                  label="分隔符号"
                  :label-col="{ span: 9 }"
                  :label-align="'left'"
                >
                  <a-select
                    v-model="delimiterSelected"
                    placeholder="请选择"
                    class="select-box"
                    size="small"
                    style="width: 316px;"
                    @change="changeSeparate"
                  >
                    <a-select-option
                      v-for="symbol in delimiterOptions"
                      :key="symbol"
                      :value="symbol"
                      >{{ symbol }}
                    </a-select-option>
                  </a-select>
                  <a-popover placement="top">
                    <template slot="content">
                      指定每条记录中用于分隔字段的分隔符。默认为逗号，并支持任意单字节字符作为分隔符。
                    </template>
                    <a-icon-font type="iconbangzhu" class="icon-help" />
                  </a-popover>
                  <a-input
                    v-if="delimiterSelected === '其他'"
                    size="small"
                    :style="{
                      width: '100px',
                      'margin-left': '20px',
                    }"
                    placeholder="请输入"
                    @change="changeSeparateInput"
                  />
                </a-form-item>
              </div>
            </div>
            <div class="import-row-general import-row-general-norequired">
              <!-- 引用符号 -->
              <div class="row-three-column">
                <a-form-item
                  label="引用符号"
                  :label-col="{ span: 9 }"
                  :label-align="'left'"
                >
                  <a-input
                    v-model="tableQuote"
                    size="small"
                    style="width: 316px;"
                    placeholder="请输入"
                    @change="changeQuote"
                  />
                  <a-popover placement="top">
                    <template slot="content">
                      指定把包裹每个字段的引用符。默认为双引号，并支持任意单字节字符作为引用符。
                    </template>
                    <a-icon-font type="iconbangzhu" class="icon-help" />
                  </a-popover>
                </a-form-item>
              </div>
            </div>
            <div class="import-row-general import-row-general-norequired">
              <!-- 转义符号 -->
              <div class="row-three-column">
                <a-form-item
                  label="转义符号"
                  :label-col="{ span: 9 }"
                  :label-align="'left'"
                >
                  <a-input
                    v-model="tableEscape"
                    size="small"
                    style="width: 316px;"
                    placeholder="请输入"
                    @change="changeEscape"
                  />
                  <a-popover placement="top">
                    <template slot="content">
                      指定添加到字段内容中出现引用符前的转义符。默认为双引号，并支持任意单字节字符作为转义符。
                    </template>
                    <a-icon-font type="iconbangzhu" class="icon-help" />
                  </a-popover>
                </a-form-item>
              </div>
            </div>
            <a-button
              style="
                font-size: 12px;
                height: 24px;
                margin-left: 107px;
                width: 200px;
              "
              :disabled="isUploading"
              @mouseover="setHover('select-file')"
              @mouseout="setHover('')"
              @click="onAdvanceSettingChange"
            >
              确定设置
            </a-button>
          </a-collapse-panel>

          <!-- 数据信息 gis 分离 （确认取消按钮） -->
          <a-collapse-panel
            v-if="(selectedDataType === 'gis' && gisVisibleConfirmButton)"
            key="4"
            header="数据信息"
          >
            <gis-data-info
              @clear-update-info="clearUpdateInfoOfGis"
              @set-selected-keys="setSelectedKeysOfGis"
            />
          </a-collapse-panel>

          <!-- 数据信息 - 数据导入配置 -->
          <a-collapse-panel
            v-if="
              (selectedDataType === 'table' &&
                datasetData &&
                datasetData.length > 0) ||
              (selectedDataType === 'database' &&
                selectedTables &&
                selectedTables.length > 0) ||
              (selectedDataType === 'http' &&
                datasetData &&
                datasetData.length > 0) ||
              (selectedDataType === 'graph' && datasetData)
            "
            key="3"
            header="数据信息"
          >
            <!-- 导入数据 那些列, 脱敏, 类型推断, 语义推断 -->
            <div
              v-if="!['graph', 'gis'].includes(selectedDataType)"
              class="import-data-set"
            >
              <div class="data-set-column">
                <a-form-item
                  v-if="selectedDataType !== 'database'"
                  label="脱敏设置"
                  :label-align="'left'"
                  :label-col="{ span: 5 }"
                >
                  <div class="desensitization-set">
                    <a-button
                      class="data-setting-button"
                      type="link"
                      @click="onDataImportConfigSettingBtnClick"
                    >
                      <a-icon-font
                        class="icon-font"
                        type="iconzidingyi"
                        style="color: #5561ff;"
                      />
                      <span>设置</span>
                    </a-button>

                    <!-- 还在完善的新需求 -->
                    <a-checkbox
                      :checked="isDataTypeInfer"
                      style="margin-left: 17px;"
                      :default-checked="true"
                      @change="changeTypeInfer"
                    >
                      推断类型
                    </a-checkbox>
                    <a-checkbox
                      :checked="isDataSemanticInfer"
                      :default-checked="false"
                      @change="changeSemanticInfer"
                    >
                      推断语义
                    </a-checkbox>
                  </div>
                </a-form-item>
                <!-- 定时设置 -->
                <a-form-item
                  v-else
                  label="脱敏/定时设置"
                  :label-align="'left'"
                  :label-col="{ span: 8 }"
                >
                  <div class="desensitization-set">
                    <a-button
                      class="data-setting-button"
                      type="link"
                      @click="onDataImportConfigSettingBtnClick"
                    >
                      <a-icon-font
                        class="icon-font"
                        type="iconzidingyi"
                        style="color: #5561ff;"
                      />
                      <span>设置</span>
                    </a-button>
                    <a-checkbox
                      :checked="isDataSemanticInfer"
                      :default-checked="false"
                      @change="changeSemanticInfer"
                    >
                      推断语义
                    </a-checkbox>
                  </div>
                </a-form-item>
              </div>
            </div>

            <!-- 本地文件导入, 预览 this.previewData -->
            <template
              v-if="
                selectedDataType === 'table' &&
                previewData &&
                previewData.length > 0
              "
            >
              <a-tabs
                v-if="previewData.length > 1"
                type="card"
                :default-active-key="
                  previewData && previewData.length > 0
                    ? previewData[0].fileName
                    : null
                "
                @change="changeTable"
              >
                <a-tab-pane
                  v-for="(table, index) in previewData"
                  :key="table.fileName"
                  :tab="table.name"
                >
                  <data-preview-table
                    :head="table.head"
                    :data="table.data"
                    data-type="table"
                    :enable-edit-title="true"
                    @changeColType="changeColType"
                    @change-semantic="changeSemantic"
                    @title-change="(value) => handleTitleChange(index, value)"
                  />
                </a-tab-pane>
              </a-tabs>
              <data-preview-table
                v-else
                :head="previewData[0].head"
                :data="previewData[0].data"
                data-type="table"
                :enable-edit-title="true"
                @changeColType="changeColType"
                @change-semantic="changeSemantic"
                @title-change="(value) => handleTitleChange(0, value)"
              />
            </template>
            <!-- 图数据 导入预览 -->
            <template v-if="selectedDataType === 'graph' && datasetData">
              <div class="data-preview-card">
                <pre
                  v-if="graphFileFormat === 'json' || graphFileFormat === 'gml'"
                >
 {{ datasetData }} </pre
                >
                <data-preview-table
                  v-else
                  :head="datasetData.head"
                  :data="datasetData.data"
                  data-type="table"
                  :enable-col-type="false"
                  :enable-semantic="false"
                >
                </data-preview-table>
              </div>
            </template>
            <!-- http 导入预览 -->
            <template v-if="selectedDataType === 'http' && datasetData">
              <data-preview-table
                :head="datasetData[0].newHead"
                :data="datasetData[0].data"
                data-type="table"
                @changeColType="changeColType"
                @change-semantic="changeSemantic"
              >
              </data-preview-table>
            </template>
            <!-- 数据库 导入预览 -->
            <a-spin :spinning="tablePreviewMask">
              <database-table-tabs
                v-if="
                  selectedDataType === 'database' &&
                  selectedTables &&
                  selectedTables.length > 0
                "
                ref="database_table_tabs"
                :dataset-data="datasetData"
                :have-set-desensitization="haveSetDesensitization"
                :database-head-semantic="databaseHeadSemantic"
                :is-set-desensitization="isSetDesensitization"
                @change-semantic="onDatabaseChangeSemantic"
                @change-table-by-active="changeTablePreviewData"
              />
              <div
                v-if="
                  (selectedDataType === 'table' &&
                    datasetData &&
                    datasetData.length > 0) ||
                  (selectedDataType === 'database' &&
                    selectedTables &&
                    selectedTables.length > 0) ||
                  (selectedDataType === 'http' &&
                    datasetData &&
                    datasetData.length > 0) ||
                  (selectedDataType === 'graph' && datasetData)
                "
                class="button-container"
                :style="{ 'margin-top': getButtonsMarginTop + 'px' }"
              >
                <a-button @click="cancelImportData">取消</a-button>
                <!-- 确认导入数据 新用户引导 step-4 -->
                <guide-step-popover :guide-step="4">
                  <template #header>
                    <a-button
                      type="primary"
                      class="button"
                      @click="handleSubmit"
                      >确定
                    </a-button>
                  </template>
                </guide-step-popover>
              </div>
            </a-spin>
          </a-collapse-panel>
        </a-collapse>
      </a-form>
    </a-spin>

    <!-- 提交失败信息 -->
    <SubmitFailModal
      :success-number="submitFailResult.successCount"
      :error-data="submitFailResult.errorLineCodeInfoMap"
      :error-number="submitFailResult.errorCount"
      @submit-data="handleErrorFileSubmit"
    >
    </SubmitFailModal>

    <!-- 脱敏设置 / 定时设置 -->
    <DatasetConfigModal
      :selected-data-type="selectedDataType"
      :selected-database-type="selectedDatabaseType"
      :selected-table-data="selectedTableData"
      :dataset-data="selectedDataType === 'graph' ? [] : datasetData"
      :have-set-desensitization="haveSetDesensitization"
      @config-submit="configSettingSubmit"
    >
    </DatasetConfigModal>
  </div>
</template>

<script lang="ts">
/**
 * page of uploading dataset
 * @author Huihua Lu
 */
import { cloneDeep } from 'lodash'
import moment from 'moment'
import { importData } from '@/api/data-list'
import { Component, Vue } from 'vue-property-decorator'
import LoadingMask from '@/components/loading-mask/loadingMask.vue'
import DataStore, { DatabaseTestState } from '@/store/modules/data'
import DataPreviewTable from '@/components/data/DatasetPreviewTable.vue'
import DatabaseModal from '@/components/data/DatabaseModal.vue'
import DatabaseTableTabs from '@/components/data/DatabaseTableTabs.vue'
import MessageModal from '@/components/Modal/MessageModal.vue'
// 脱敏设置弹窗
import DesensitizationSet from '@/components/data/DesensitizationSet.vue'
import { getTablesHead } from '@/api/data'
import { HttpCreateTable } from '@/api/data-import'
import GuideStepPopover from '@/components/user-guide/GuideStepPopover.vue'
import UserGuideStore from '@/store/modules/user-guide'
import HttpModal from './HttpModal.vue'
import TimeSetting from './TimingSetting.vue'
import SubmitFailModal from './SubmitFailModal.vue'
import DatasetConfigModal from './DatasetConfigModal.vue'
import GisModal from '@/components/data/gis/GisModal.vue' // gis 表单模块
import GisDataInfo from '@/components/data/gis/GisDataInfo.vue'

interface KeyValue {
  [key: string]: any
}

@Component({
  components: {
    LoadingMask,
    DataPreviewTable,
    DatabaseModal,
    DatabaseTableTabs,
    MessageModal,
    HttpModal,
    DesensitizationSet,
    TimeSetting,
    SubmitFailModal,
    DatasetConfigModal,
    GuideStepPopover,
    GisModal,
    GisDataInfo,
  },
})
export default class DataUploadPage extends Vue {
  // 折叠面板相关参数
  private collapseActiveKey: Array<string> = ['1', '2', '3', '4']

  private selectedDataType: string = 'table'
  private selectedDatabaseType: string = 'mysql'
  // 目前选中的数据库中的数据表
  private selectedDatabaseTables = []
  // 选中的预览数据表的表明
  private selectedPreviewTableName: string = ''

  // 数据库图片
  private databaseImg: any = {}

  // 上传文件
  private fileList: Array<any> = []
  private filename: string = ''
  private fileType: string = ''
  private fileMd5: string = ''
  private fileCodingType: string = 'utf-8'
  private datasetData: any = null // 传入预览表的数据（在预览组件中有用到）原数据
  private previewData: any = null // 传入预览表的数据（表格文件 导入 数据预览数据）
  // previewData原因： 设置脱敏的接口需要将原数据进行上传， 接口 dataset/generateMask params: colData

  private activeTableFileName: string = ''
  // 上传蒙层
  private spinning: boolean = false
  private spinningMessage = ''
  // hover的部分
  private hover: string = ''
  // 是否已选择数据库
  private isDatabaseChosen = false
  // 数据库配置相关内容
  private databaseConfigValidateStatus = 'error'
  private databaseConfigValidateMessage = ''

  private form: any

  private categoryRequired: boolean = false

  private tablePreviewMask = false

  // 第一行是否作为字段名
  private firstLineAsFields = true
  // 数据集名称清除icon
  private datasetNameClearStatus = false
  // 保存当前dataset的值，从mysql转过来的时候能显示之前的值
  private datasetName = ''
  public $refs!: {
    datasetName: any
  }
  // // 脱敏设置弹窗显示
  // public DesensitizationSetVisible: boolean = false
  // 已进行脱敏设置并保存
  private isSetDesensitization: boolean = false

  public databaseHeadSemantic: any = {} // 数据库导入时记录的语义，按照表名进行划分

  private databaseType: Array<string> = [] //  外部数据库类型列表
  private graphFileFormat: string = '' // 图文件格式，json or csv

  // 高级设置部分数据
  // 分隔符号
  private tableSeparate: string = ','
  private delimiterOptions = [',', 'tab', 'space', '其他']
  private delimiterSelected = ','
  // 引用符号
  private tableQuote: string = '"'
  // 转义符号
  private tableEscape: string = '"'

  // 提交失败结果
  private submitFailResult: any = {}
  private submitFailCount: number = 0

  // 语义类型推断
  private isDataTypeInfer: boolean = true
  private isDataSemanticInfer: boolean = false

  // 上传说明是否折叠(图文件)
  private isNoteUnfold: boolean = false

  public fileSizeLimit: any = {
    // 文件大小限制映射
    table: 'csv——10G，excel——1G。若excel上传失败可另存为csv格式再次尝试',
    http: '100MB',
    gis: '1GB',
  }

  beforeCreate() {
    this.form = this.$form.createForm(this, { name: 'upload_form' })
    DataStore.queryListDataLevel()
    DataStore.queryListMaskingType()
    const response = DataStore.getDatabaseTypeImg({ type: null })
    response.then((result: any) => {
      this.databaseImg = result.result
    })
  }

  // 新用户引导状态
  public get newUserGuideMode() {
    return UserGuideStore.newUserGuideMode
  }

  /**
   * 设置指定步骤  新用户引导进入下一步
   */
  public updateDataSuccessToGuideStep(step: number) {
    UserGuideStore.toSpecifiedGuideStep(step)
  }

  /**
   * 修改新用户引导状态
   * true: 当前步骤正在进行
   */
  public changeGuideStatus(status: boolean) {
    UserGuideStore.setCurrentGuideStepInProgress(status)
  }

  mounted() {
    // this.getListExternalDatabaseType()
    // DataStore.setSubmitFailModalVisible(true)
    const lastUrlStorage: any = sessionStorage.getItem('lastUrl')
    if (lastUrlStorage !== undefined && lastUrlStorage !== null) {
      const { loadType } = JSON.parse(lastUrlStorage)
      this.selectedDataType = loadType
    }
  }

  public changeNoteState() {
    this.isNoteUnfold = !this.isNoteUnfold
  }

  /** 数据表更改 */
  public dataTableChange(data: any) {
    console.log('data-------------->', data)
  }

  // public async getListExternalDatabaseType() {
  //   const response: any = await getListExternalDatabaseType()
  //   if (response.data.code === 100) {
  //     this.databaseType = response.data.result
  //   }
  // }

  // 可选的编码形式
  public get datasetCodingType() {
    return DataStore.datasetCodingType
  }

  // 当前选择的分类
  public selectedCategory: string = ''

  // 可选的目录
  public get categoryOptions() {
    if (DataStore.datasetCategory) {
      return DataStore.datasetCategory.map((d) => d.categoryName)
    }
    return []
  }

  // select-option里的项
  public get categorySelectOptions() {
    const result = [...this.categoryOptions].reverse() as string[]
    if (!result.includes(this.selectedCategory)) {
      ;[this.selectedCategory] = result
    }
    return result
  }

  // 数据库链接测试状态
  public get testState() {
    return DataStore.testState
  }

  // 数据库的所有表
  public get databaseTables() {
    return DataStore.databaseTables
  }

  // 用户选中的数据库表
  public get selectedTables() {
    return DataStore.selectedTables
  }

  // 获取的表数据
  public get selectedTableData() {
    return DataStore.selectedTableData
  }

  // 获取http图片
  public get httpButtonImg() {
    const httpElement = this.databaseImg.find((element: any) => {
      return element.type === 'http'
    })
    return httpElement.logo
  }

  public get databaseButtonImg() {
    const result: any[] = []
    this.databaseImg.forEach((element: any) => {
      if (element.type !== 'http') {
        result.push(element)
      }
    })
    return result
  }

  // 取消上传数据则清空一切与上传数据有关的State（暂不支持分步取消）
  public cancelImportData() {
    let lastUrl: any = sessionStorage.getItem('lastUrl')
    if (lastUrl !== undefined && lastUrl !== null) {
      lastUrl = JSON.parse(lastUrl)
      const { href } = this.$router.resolve({
        path: lastUrl.url,
      })
      window.location.href = href
      sessionStorage.removeItem('lastUrl')
    } else {
      const lastId =
        DataStore.preSelectedDataset.length > 0
          ? DataStore.preSelectedDataset.slice(-1)[0].id
          : -1
      DataStore.cancelImport()
      DataStore.setSelectedDatasetId(lastId)
      this.$router.push('/management/data')
      // 在 用户引导状态下 预览数据， 取消导入， 提示步骤回到 step-2
      this.updateDataSuccessToGuideStep(2)
    }
  }

  // 修改数据类型（表格型or数据库）
  public changeDataType(value: string) {
    // 在 用户引导状态下 当还没有确认导入数据前， 切换数据类型， 提示步骤回到 step-3
    this.updateDataSuccessToGuideStep(3)
    if (this.newUserGuideMode && value === 'graph') {
      this.$message.warn('新用户引导过程为更好体验禁止选择图文件！')
      return
    }
    this.selectedDataType = value
    DataStore.setSelectedImportType(value)
    this.resetData()
    // DataStore.cancelImport()
  }

  /**
   * 清除数据、设置
   */
  public resetData() {
    this.datasetData = null
    this.fileList = []
    this.clearDatasetName()
    DataStore.setSelectedTableData(null)
    DataStore.setSelectedTables([])
  }

  // 改变hover
  public setHover(value: string) {
    this.hover = value
  }

  // 修改数据库类型（目前仅支持mysql）
  public changeDatabaseType($event: any) {
    this.selectedDatabaseType = $event.target.value
    this.databaseHeadSemantic = {}
    DataStore.setSelectedTables([])
  }

  // 代开http导入Modal
  public onHttpSettingBtnClick() {
    DataStore.setHttpModalVisible(true)
  }

  // 提交httpModal
  public onHttpModalSubmit(data: any) {
    data.result.newHead = cloneDeep(data.result.head)
    data.result.fileName = data.datasetName
    this.datasetData = [cloneDeep(data.result)]
    this.datasetData = this.datasetData.map((item: any) => {
      return {
        ...item,
        newHead: item.head,
      }
    })
    // 上传完成 初始化 导入设置 是否导入 是否脱敏等 供配置弹窗使用
    this.initDesensitizationList(this.datasetData, 3)
    this.activeTableFileName = data.datasetName
    // 进入下一步
    this.updateDataSuccessToGuideStep(4)
  }

  // 删除当前的文件
  public deleteFile() {
    this.isCancel = true
    this.isUploading = false
    this.fileList.pop()
    this.fileType = ''
    this.datasetData = null
    this.haveSetDesensitization = {} // 在删除 文件的同时清空配置
    this.form.setFieldsValue({ datasetName: '' })
    this.datasetName = ''
    this.datasetNameClearStatus = false
  }

  // 获取底部按钮的margin-top
  public get getButtonsMarginTop() {
    // 正在展示数据表，则跟在后面
    if (
      (this.selectedDataType === 'table' && this.datasetData) ||
      (this.selectedTables && this.selectedTables.length > 0) ||
      (this.selectedDataType === 'graph' && this.datasetData)
    ) {
      return '16'
    }
    const top = 279
    // csv导入
    if (this.selectedDataType === 'table') {
      return top - 77
    }
    // mysql导入
    return top
  }

  // 获取当前的数据库
  public get currentUploadDatabaseName() {
    return DataStore.databaseConfig.databaseName
  }

  // 校验，是否选定了数据库
  public validateDatabase(rule: any, value: any, callback: any) {
    if (
      !this.isDatabaseChosen ||
      (this.isDatabaseChosen && this.databaseTables.length === 0)
    ) {
      callback('请配置数据库！')
    }
    callback()
  }

  private graphData: any = null // 无论上传什么数据都会被记录，该变量只被当作图数据被使用

  /**
   * 选择文件上传文件 / 修改编码 / 修改是否使用首行做表头都需要上传文件 获取预览数据
   */
  public async uploadDataset(option: any, md5: string = '') {
    // 获取文件名和文件类型
    ;[this.filename, this.fileType] = option.file.name.toString().split('.')
    if (this.filename !== '') {
      this.datasetNameClearStatus = true
    }
    const formData = new window.FormData()
    formData.append('filename', option.file.name)
    formData.append('charSet', this.fileCodingType)
    formData.append('separate', this.tableSeparate)
    formData.append('quote', this.tableQuote)
    formData.append('escape', this.tableEscape)
    formData.append('firstLineAsFields', String(this.firstLineAsFields)) // 第一行是否作为数据表的列字段名称
    // String(this.firstLineAsFields) boolean类型的参数不能赋值给string | Blob类型的参数。
    this.graphData = formData

    this.isCancel = false
    this.isUploading = true

    try {
      // 只有csv需要分块上传
      if (this.fileType === 'csv') {
        this.loadingProgress = 0
        this.uploadingState = 'active'
        this.loadingText = '导入中'

        const chunkSize = 5242880 // 5MB

        const totalChunks: any = Math.ceil(option.file.size / chunkSize) // 块数
        if (md5 === '') {
          // 扫描文件计算文件MD5值
          this.fileMd5 = (await this.scanningFile(option.file)) as string
        } else {
          // 如果是传入了非空的md5值，则说明是在文件编码类型切换调用的，所以直接上传就好了
          this.fileMd5 = md5
          this.loadingText = '扫描完成'
          this.uploadingState = 'complete'
        }

        formData.append('totalChunks', totalChunks) // 块数
        formData.append('identifier', this.fileMd5)

        // 如果只剩一块则直接上传
        if (totalChunks <= 1) {
          this.loadingProgress = 100
          formData.append('file', option.file)
          formData.set('chunkNumber', '0')
          await this.uploadFile(formData, option)
          this.isCancel = false
          this.isUploading = false
        } else {
          // 检查文件md5值
          const response = await DataStore.checkFileMd5(formData)
          if (response.data.code === 100) {
            const { result } = response.data
            const { code, data, missChunks } = result
            if (code === 200) {
              // 文件已经存在
              this.datasetData = data
              this.loadingProgress = 100
              this.fileUpLoadEnd(option)
            } else if (code === 206) {
              // 文件上传了一部分
              // 先获取已经上传过的文件块的处理进度
              const checkProgress = await this.checkUploadedChunksProgress(
                formData,
                totalChunks
              )
              this.loadingProgress = Math.floor(
                (100 * (totalChunks - missChunks.length)) / totalChunks
              )
              // 判断code是200还是206
              if (checkProgress.code === 200) {
                this.datasetData = checkProgress.data
                this.loadingProgress = 100
                this.fileUpLoadEnd(option)
              } else if (checkProgress.code === 206) {
                // 开始上传未上传的文件块
                await this.uploadChunkFile(
                  option.file,
                  formData,
                  option,
                  missChunks
                )
              } else if (code === 405) {
                this.$message.warning('该环节出现错误，请重复执行')
              }
            } else if (code === 404) {
              // 文件没有上传过
              const allChunks = []
              for (let i = 0; i < totalChunks; i += 1) {
                allChunks.push(i)
              }
              await this.uploadChunkFile(
                option.file,
                formData,
                option,
                allChunks
              )
            } else if (code === 405) {
              this.$message.warning('该环节出现错误，请重复执行')
            }
          }
          // 如果没有请求成功，则打印报错信息
          else if (response.data.tips) {
            this.$message.warning(response.data.tips)
          } else {
            this.$message.warning(response.data.message)
          }
          this.isUploading = false
        }
      } else {
        // excel文件和图文件不需要分块上传
        this.spinningMessage = '导入中'
        this.spinning = true
        formData.append('file', option.file)
        await this.uploadFile(formData, option)
        if (this.fileType === 'xls' || this.fileType === 'xlsx') {
          this.fileUpLoadEnd(option)
        }
      }
    } catch (error) {
      console.log(error)
    }
  }

  // 大文件上传
  private uploadingState: string = 'active' // 加载状态
  private loadingProgress: number = 0 // 加载进度
  private loadingText: string = '导入中' // 加载文本
  private isCancel: boolean = true // 是否取消上传
  private isUploading: boolean = false // 是否正在上传

  // 扫描文件并计算MD5值
  public scanningFile(file: any) {
    return new Promise((resolve, reject) => {
      this.loadingProgress = 0
      this.uploadingState = 'active'
      this.loadingText = '扫描中'

      // 计算文件md5值
      const SparkMD5 = require('spark-md5')
      const blobSlice = File.prototype.slice
      const chunkSize = 5242880 // 5MB
      const chunks = Math.ceil(file.size / chunkSize)
      const spark = new SparkMD5.ArrayBuffer()
      const fileReader = new FileReader()
      let currentChunk = 0

      const loadNext = () => {
        const start = currentChunk * chunkSize
        const end =
          start + chunkSize >= file.size ? file.size : start + chunkSize
        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
      }

      fileReader.addEventListener('load', (event: any) => {
        spark.append(event.target.result) // Append array buffer
        currentChunk += 1

        if (this.isCancel) {
          reject()
        }

        this.loadingProgress = Math.floor((100 * currentChunk) / chunks)
        if (currentChunk < chunks) {
          loadNext()
        } else {
          const md5: string = spark.end() // 得到md5
          spark.destroy() // 释放缓存
          this.loadingText = '扫描完成'
          this.uploadingState = 'complete'
          resolve(md5)
        }
      })

      fileReader.addEventListener('error', () => {
        console.warn('oops, something went wrong.')
        file.cancel()
        this.loadingText = '扫描失败'
        this.uploadingState = 'exception'
      })

      loadNext()
    })
  }

  // 上传文件
  public async uploadFile(formData: any, option: any) {
    // 更新默认推断状态
    this.isDataTypeInfer = true
    this.isDataSemanticInfer = false
    try {
      if (this.selectedDataType === 'graph') {
        const response = await DataStore.uploadGraphFile(formData)
        this.spinning = false
        if (response.data.code === 100) {
          const { format, content } = response.data.result
          this.graphFileFormat = format
          if (format === 'json') {
            this.datasetData =
              content || 'not support preview for other file yet'
          } else if (format === 'csv' || format === 'gml') {
            this.datasetData = content
          }
        } else {
          this.fileList = []
          this.datasetData = null
          this.spinning = false
          if (response.data.tip) {
            this.$message.warning(response.data.tip)
          }
          return
        }
      } else {
        // csv excel file upload
        const response = await DataStore.uploadDatasetFile(formData)
        this.spinning = false
        if (response.data.code === 100) {
          const { result } = response.data
          this.datasetData = result.data // 预览数据
        } else {
          // this.fileList = []
          this.datasetData = null
          if (response.data.tips) {
            this.$message.warning(response.data.tips)
          } else {
            this.$message.warning(response.data.message)
          }
          return
        }
      }
    } catch (error) {
      this.spinning = false
      this.fileList = []
      this.datasetData = null
      this.$message.error('数据上传失败，请稍后重试')
      this.loadingText = '导入失败'
      this.uploadingState = 'exception'
      console.error(`upload error: ${error}`)
      return
    }
    // 是 csv excel 表格文件上传
    if (this.selectedDataType !== 'graph') {
      this.fileUpLoadEnd(option)
    }
  }

  // 获取已经上传的文件块的处理进度
  public async checkUploadedChunksProgress(formData: any, totalChunks: number) {
    this.uploadingState = 'active'
    this.loadingText = '校验中'

    // 每隔timedelay毫秒发送一次请求
    const checkProgressInInterval = (timeDelay: number) => {
      return new Promise((resolve) => {
        setTimeout(async () => {
          const response = await DataStore.checkProgress(formData)
          if (response.data.code === 100) {
            const { result } = response.data
            const { code, progress, data } = result
            // 如果progress和result都为空，则还没开始处理，如果progress为空，result不为空，则表示上传结束
            if (progress === null) {
              resolve({ code, data, success: true })
            } else {
              this.loadingProgress = Math.floor((100 * progress) / totalChunks)
              resolve({ code, data, success: false })
            }
          }
        }, timeDelay)
      })
    }

    // eslint-disable-next-line no-constant-condition
    while (true) {
      if (this.isCancel) {
        throw new Error('file upload cancel')
      }
      try {
        // eslint-disable-next-line no-await-in-loop
        const { code, data, success }: any = await checkProgressInInterval(500)
        if (success) {
          return { code, data }
        }
      } catch (error) {
        throw new Error(error)
      }
    }
  }

  /* *
   * 上传分块文件
   *  首先上传第一块文件，用于检查文件表头信息
   *  如果第一块文件上传成功，则分10个块为1组，同步上传
   *  都上传成功后，再上传最后一个文件
   * */
  public async uploadChunkFile(
    file: any,
    formData: any,
    option: any,
    missChunks: any[]
  ) {
    this.uploadingState = 'active'
    this.loadingText = '导入中'
    let allSuccess: boolean = false // 是否全部导入成功

    const blobSlice = File.prototype.slice
    const chunkSize = 5242880 // 5MB
    const chunks = Math.ceil(file.size / chunkSize)
    let completeChunks = chunks - missChunks.length
    this.loadingProgress = Math.floor((100 * completeChunks) / chunks)

    // 加载分块文件
    const loadChunkFile = (chunkIndex: number) => {
      const start = chunkIndex * chunkSize
      const end = start + chunkSize >= file.size ? file.size : start + chunkSize
      return blobSlice.call(file, start, end)
    }

    formData.append('chunkNumber', 0)
    formData.append('chunkSize', chunkSize)
    formData.append('file', option.file)

    // 临时数组，保存最后渲染的数据
    let temporaryData: any = []

    // 封装请求一组发送
    const requestLists = []
    const firstChunk = missChunks[0]
    let lastChunk = null
    if (missChunks.length > 1) {
      lastChunk = missChunks[missChunks.length - 1]
    }
    missChunks = missChunks.slice(0, -1)
    for (let i = 1; i < missChunks.length; i += 10) {
      requestLists.push(missChunks.slice(i, i + 10))
    }

    // 同时请求
    const requestUploadFileChunks = (requests: number[]) => {
      return new Promise((resolve, reject) => {
        let success: boolean = true
        let passedRequset = 0 // 已经返回的请求数

        // 对每个文件片进行请求
        requests.forEach((chunkIndex: number) => {
          // 隔30ms发送一次，减少服务器压力
          setTimeout(() => {
            const chunkFile = loadChunkFile(chunkIndex)
            formData.set('file', chunkFile)
            formData.set('chunkNumber', chunkIndex.toString())
            DataStore.uploadDatasetFile(formData).then(
              (response: any) => {
                passedRequset += 1
                // 如果中途取消了
                if (this.isCancel) {
                  reject()
                }
                if (response.data.code === 100) {
                  completeChunks += 1
                  this.loadingProgress = Math.floor(
                    (100 * completeChunks) / chunks
                  )
                  // 后端返回上传成功，则获取数据
                  if (response.data.result.complete) {
                    allSuccess = true
                    temporaryData = response.data.result.data // 预览数据
                  }
                } else {
                  success = false
                }
                // 如果当前所有请求完了
                if (passedRequset === requests.length) {
                  resolve(success)
                }
              },
              () => {
                passedRequset += 1
                success = false
              }
            )
          }, 30)
        })
      })
    }

    try {
      // 先上传第一个块，如果第一个请求失败了，就不继续上传了
      const firstSuccess = await requestUploadFileChunks([firstChunk])
      if (firstSuccess) {
        // eslint-disable-next-line no-restricted-syntax
        for (const list of requestLists) {
          // eslint-disable-next-line no-await-in-loop
          await requestUploadFileChunks(list)
        }

        if (lastChunk !== null) {
          // 最后上传最后一个
          await requestUploadFileChunks([lastChunk])
        }
      }
    } catch (error) {
      console.log(error)
      return
    }

    // 所有请求都返回了
    if (allSuccess) {
      this.datasetData = temporaryData
      this.fileUpLoadEnd(option)
    } else {
      this.uploadingState = 'exception'
      this.loadingText = '导入失败'
    }
  }

  // csv和excel文件上传完毕
  public fileUpLoadEnd(option: any) {
    // data upload success, user guide to the next step
    this.updateDataSuccessToGuideStep(4)

    this.form.setFieldsValue({ datasetName: this.filename })
    this.datasetName = this.filename
    // this.$refs.datasetName.querySelectorAll('input')[0].focus()

    this.datasetData = this.datasetData.map((item: any) => {
      return {
        ...item,
        newHead: item.head,
      }
    })
    // 预览数据
    if (this.datasetData.length > 0) {
      this.previewData = cloneDeep(this.datasetData)
    }
    // 上传完成 初始化 导入设置 是否导入 是否脱敏等 供配置弹窗使用
    this.initDesensitizationList(this.datasetData, 1)
    this.activeTableFileName = this.datasetData[0].fileName
    // 通过 form 组件调用
    if (option.onSuccess) {
      option.onSuccess()
    }
    this.loadingText = '导入完成'
    this.loadingProgress = 100
    this.uploadingState = 'complete'
    this.isCancel = false
    this.isUploading = false
  }

  /**
   * 上传前限制文件大小
   */
  public beforeUpload(file: any) {
    const [, fileType] = file.name.toString().split('.')
    let fileSizeAvailiable = true
    // 如果为csv文件，限制大小为20G
    if (fileType === 'csv') {
      fileSizeAvailiable = file.size / 1024 / 1024 / 1024 / 20 < 1
      if (!fileSizeAvailiable) {
        this.$message.error('超过文件大小限制（20GB）!')
      }
    } else {
      fileSizeAvailiable = file.size / 1024 / 1024 / 1024 < 1
      if (!fileSizeAvailiable) {
        this.$message.error('超过文件大小限制（1GB）!')
      }
    }

    return fileSizeAvailiable
  }

  /**
   * 只能上传一个文件，重复上传会覆盖
   */
  public async handleUpload(info: any) {
    this.fileList = [...info.fileList].slice(-1)
    if (info.fileList.length === 0) {
      this.datasetData = null
      this.fileType = ''
    } else {
      // 获取类型
      this.fileType = this.fileList[0].name.slice(
        this.fileList[0].name.lastIndexOf('.') + 1
      )

      this.tableSeparate = this.fileType === 'csv' ? ',' : ''
      this.tableQuote = this.fileType === 'csv' ? '"' : ''
      this.tableEscape = this.fileType === 'csv' ? '"' : ''
    }
  }

  /**
   * 上传的数据文件信息
   * 由于每次修改是否使用首行作为字段名都将文件上传，所以不需要之前的判断返回在导入的时候准备的两份预览数据
   * （首行做字段名 和 不做字段名 的预览数据）
   */
  public get uploadDatasetData() {
    return this.datasetData
  }

  // 修改数据列的类型
  public changeColType(value: any) {
    const index = this.datasetData.findIndex(
      (item: any) => item.fileName === this.activeTableFileName
    )
    this.datasetData[index].newHead[value.index].type = value.type
    // 上传表格文件预览数据
    this.previewData[index].newHead[value.index].type = value.type
  }

  /**
   * 语义推断与修改
   */
  public changeSemantic(semanticInfo: { index: number; semantic: string }) {
    const { index, semantic } = semanticInfo
    // 当前激活的表
    const indexCol = this.datasetData.findIndex(
      (item: any) => item.fileName === this.activeTableFileName
    )
    this.datasetData[indexCol].newHead[index].semantic = semantic
    this.datasetData[indexCol].head.splice(index, 1, {
      ...this.datasetData[indexCol].head[index],
      semantic,
    })

    // 上传表格文件预览数据
    this.previewData[indexCol].newHead[index].semantic = semantic
    this.previewData[indexCol].head.splice(index, 1, {
      ...this.previewData[indexCol].head[index],
      semantic,
    })
  }

  /** 定时设置 */
  public timingSettingMaps: any

  /** 定时设置，提交数据 */
  public timingSetingSubmit(data: any) {
    this.timingSettingMaps = data
  }

  /**
   * 外部数据库 选择的数据表 变化
   */
  public changeSelectTables(values: any) {
    DataStore.setSelectedTables(values)
    this.updateTables(values, 'select')
  }

  /**
   * 数据库更新数据表预览信息, 根据选择的 tables
   * 两个来源：
   *  1.选择表 - select
   *  2.修改脱敏配置 - 'setting' （不需要更新所有表的表头信息 getTablesHead ）
   */
  public async updateTables(values: any, form: string) {
    if (values.length === 0) {
      return
    }
    DataStore.setSelectedTables(values)
    if (values && values.length > 0) {
      this.isDataSemanticInfer = false // 初始化 不使用语义推荐
      this.updateTablePreviewMask(true)
      const tableName = values[values.length - 1] as string
      // 设置激活的表格,因为用了v-if 所以必须放到下一轮微任务来激活对应表格，否则$refs引用不到
      this.$nextTick(() => {
        const dataBaseTableTabs = (this.$refs as any).database_table_tabs as any
        if (
          dataBaseTableTabs &&
          typeof dataBaseTableTabs.setDataTable === 'function'
        ) {
          dataBaseTableTabs.setDataTable(tableName)
        }
      })
      try {
        const result = await this.changeTablePreviewData(tableName)
        this.updateTablePreviewMask(false)
        if (result && result.data.length === 0) {
          this.$message.warn('该数据表为空！')
        }
      } catch (error) {
        console.log('getTableData error:', error)
        this.updateTablePreviewMask(false)
      }
    }
    if (form === 'select') {
      await this.getTablesHead(values)
    }
    // 用户引导进入下一步
    this.updateDataSuccessToGuideStep(4)
  }

  /**
   * 根据用户选择请求数据进行预览
   * 唯一一个数据库数据预览的接口获取
   */
  public async changeTablePreviewData(table: string) {
    // 修改当前选中的 table，并改变遮罩状态
    this.updateTablePreviewState(table, true)
    const maskData: any = []

    if (this.haveSetDesensitization[table]) {
      const columnNames = Object.keys(this.haveSetDesensitization[table])
      columnNames.forEach((item) => {
        maskData.push({
          dataLevelId: this.haveSetDesensitization[table][item].masking
            ? this.haveSetDesensitization[table][item].dataLevelId
            : null,
          dataMaskingType: this.haveSetDesensitization[table][item].masking
            ? this.haveSetDesensitization[table][item].dataMaskingType
            : null,
          importColumn: this.haveSetDesensitization[table][item].importColumn,
          masking: this.haveSetDesensitization[table][item].masking,
          name: item,
          semantic:
            this.databaseHeadSemantic[table][item].semantic ||
            this.databaseHeadSemantic[table][item].recommendSemantic,
          type: this.databaseHeadSemantic[table][item].type,
        })
      })
    }

    let passData = {
      ...DataStore.databaseConfig,
      tableName: table,
    }

    if (maskData.length !== 0) {
      passData = {
        ...passData,
        baseTableConfigVO: {
          data: maskData,
        },
      }
    }
    let returnResult: any = null // 预览数据
    let DataBaseTableTabsComponent: any = null // 数据库预览子组件
    try {
      returnResult = await DataStore.getTablePreviewData(passData)
      this.updateTablePreviewState(table, false)
      // 设置激活的表格,因为用了v-if 所以必须放到下一轮微任务来激活对应表格，否则$refs引用不到
      this.$nextTick(() => {
        DataBaseTableTabsComponent = (this.$refs as any)
          .database_table_tabs as any
        if (
          DataBaseTableTabsComponent &&
          typeof DataBaseTableTabsComponent.updateActiveTableAndLoading ===
            'function'
        ) {
          DataBaseTableTabsComponent.updateActiveTableAndLoading(true, table)
        }
      })
    } catch (error) {
      DataBaseTableTabsComponent?.updateActiveTableAndLoading(false)
      console.error('getTableData error:', error)
      this.$message.error('获取表格数据失败，请刷新重试')
      this.updateTablePreviewState(table, false)
    }
    return returnResult
  }

  /**
   * http接入/数据类型改变
   * 目前仅支持 json
   */
  dataTypeChanged(data: any) {
    console.log('dataTypeChanged', data)
  }

  /**
   * 编码方式改变
   * （截止2021.08.25只有数据类型为 表格文件在用）
   * 编码方式改变也要重新上传文件
   */
  public async codingTypeChanged(data: string) {
    this.fileCodingType = data
    // 当已经选择文件后
    if (this.fileList.length > 0) {
      const fileOption: any = {
        file: this.fileList[0].originFileObj,
        filename: this.filename,
      }
      await this.uploadDataset(fileOption, this.fileMd5)
    }
  }

  // -------  js 添加 start

  // 已设置的脱敏设置  与导入设置
  public haveSetDesensitization: any = {}

  /**
   * 外部数据库导入数据定时设置按钮点击事件
   */
  public async onDataImportUpdateSettingBtnClick() {
    DataStore.setTimingSettingModalVisible(true)
  }

  /**
   * 外部数据库导入数据脱敏/定时设置按钮点击事件
   */
  public async onDataImportConfigSettingBtnClick() {
    if (
      DataStore.getListMaskingType.length > 0 &&
      DataStore.getListDataLevel.length > 0
    ) {
      DataStore.setDatasetConfigModalVisible(true)
    } else {
      DataStore.queryListDataLevel()
      DataStore.queryListMaskingType()
      DataStore.setDatasetConfigModalVisible(true)
    }
  }

  public configSettingSubmit(data: any) {
    if (this.selectedDataType === 'database') {
      this.confirmDesensitizationSet(data.desensitization)
      this.timingSetingSubmit(data.timing)
    } else {
      this.confirmDesensitizationSet(data.desensitization)
    }
    DataStore.setIsConfigSubmit(false)
  }

  /**
   * 初始化 脱敏列表 默认 全部字段（列） 导入 不脱敏, 多张表 中字段（列）是都导入 是否脱敏 脱敏类型 脱敏方式
   * {
   *   tableName: { columnName: {dataLevelId: 脱敏类型, dataMaskingType: 'md5', importColumn: 是否导入, masking: 脱敏}  }
   * }
   * @param tables
   * @param type 类型 1- 上传文件  2- 外部数据库
   * @author Jinhui Hou
   */
  public initDesensitizationList(tables: any, type: number) {
    const setDesensitization: any = cloneDeep(this.haveSetDesensitization) // 历史脱敏配置的备份
    this.haveSetDesensitization = {} // init
    if (tables) {
      tables.forEach((table: any) => {
        let columnMap: any = {} // 每张表对应的脱敏配置
        // 历史脱敏配置中是否有当前表
        if (!setDesensitization[table.tableName]) {
          table.head.forEach((column: any) => {
            if (typeof column === 'string') {
              columnMap[column] = {
                dataLevelId: 1,
                dataMaskingType: 'md5',
                importColumn: true,
                masking: false,
              } // 外部数据库
            } else {
              columnMap[column.name] = {
                dataLevelId: 1,
                dataMaskingType: 'md5',
                importColumn: true,
                masking: false,
              } // 本地文件上传
            }
          })
        } else {
          // 应对 当选择两张表， 且脱敏信息都已经配置， 删除一个， 则另一个在初始化的时候保留上次配置
          columnMap = setDesensitization[table.tableName]
        }

        if (type === 1) {
          this.haveSetDesensitization[table.name] = columnMap // 本地文件上传
        } else if (type === 2) {
          this.haveSetDesensitization[table.tableName] = columnMap // 外部数据库
        } else if (type === 3) {
          this.haveSetDesensitization[table.fileName] = columnMap // http接口
        }
      })
    } else {
      this.haveSetDesensitization = {}
    }
  }

  /**
   * 外部数据库 获取数据表的表头， 用来初始化 脱敏配置项， 数据类型、语义推荐
   * @author Jinhui Hou
   */
  public async getTablesHead(tableNames: any) {
    const parameters = {
      ...DataStore.databaseConfig,
      tableNames,
    }
    const response: any = await getTablesHead({ data: parameters })
    if (response.data.code === 100) {
      this.initDesensitizationList(response.data.result, 2)
      this.initDatabaseSemantic(response.data.result)
    }
  }

  /**
   * 确认脱敏设置, 即保存了脱敏配置
   */
  public confirmDesensitizationSet(fieldsList: any) {
    this.haveSetDesensitization = fieldsList
    DataStore.setIsSaveDesensitization(true)
    // 上传数据文件
    if (this.selectedDataType === 'table') {
      this.getDataWithMask()
    }
    // 数据库 （修改了脱敏配置）， 更新数据表预览
    const tables: string[] = Object.keys(fieldsList)
    if (this.selectedDataType === 'database' && tables.length > 0) {
      this.updateTables(tables, 'setting')
    }
  }

  public getDataWithMask() {
    const maskData: any = []
    const tableNames = Object.keys(this.haveSetDesensitization)
    const fileNames: any = []
    this.datasetData.forEach((element: any) => {
      fileNames.push(element.fileName)
    })
    const index =
      this.datasetData.length > 0
        ? fileNames.findIndex((item: any) => item === this.activeTableFileName)
        : 0
    const table = tableNames[index]
    const columns = this.datasetData[index].newHead
    if (this.haveSetDesensitization[table]) {
      columns.forEach((item: any) => {
        maskData.push({
          dataLevelId: this.haveSetDesensitization[table][item.name].masking
            ? this.haveSetDesensitization[table][item.name].dataLevelId
            : null,
          dataMaskingType: this.haveSetDesensitization[table][item.name].masking
            ? this.haveSetDesensitization[table][item.name].dataMaskingType
            : null,
          importColumn: this.haveSetDesensitization[table][item.name]
            .importColumn,
          name: item.name,
          semantic: item.semantic || item.recommendSemantic,
          type: item.type,
        })
      })
    }

    const passData = {
      baseTableConfigVO: {
        data: maskData,
      },
      colData: this.datasetData[index].data,
    }

    // 设置脱敏后， 将加密后的数据尽心显示， <表格文件>
    DataStore.generateMask(passData).then((response) => {
      this.previewData[index].head = response.data.result.head
      this.previewData[index].newHead = response.data.result.head
      this.previewData[index].data = response.data.result.data
    })
  }

  // -------  js 添加  end

  // 更新数据表信息遮罩状态
  public updateTablePreviewMask(state: boolean) {
    this.tablePreviewMask = state
  }

  // 更新数据表相关信息
  public updateTablePreviewState(tableName: string, state: boolean) {
    this.selectedPreviewTableName = tableName

    if (!state) {
      // tab切换后数据加载ready

      const { head } = this.selectedTableData
      const targetSemantic = this.databaseHeadSemantic[tableName]
      if (targetSemantic) {
        head.forEach((column: any) => {
          // targetSemantic[column.name] 有可能为undefined
          column.recommendSemantic =
            targetSemantic[column.name]?.recommendSemantic
          column.semantic = targetSemantic[column.name]?.semantic
        })
        DataStore.setSelectedTableData({
          ...this.selectedTableData,
          head,
        })
      } else {
        console.error('targetSemantic error', targetSemantic)
      }
    }
    this.updateTablePreviewMask(state)
    DataStore.setIsSaveDesensitization(false)
  }

  /**
   * 修改是否 将第一行作为数据作为的列字段名称
   * （数据类型为： 表格文件 时使用）
   */
  public async changeFirstLine() {
    this.firstLineAsFields = !this.firstLineAsFields
    if (this.fileList?.length > 0) {
      const fileOption: any = {
        file: this.fileList[0].originFileObj,
        filename: this.filename,
      }
      await this.uploadDataset(fileOption)
    }
    if (this.datasetData?.length > 0) {
      this.activeTableFileName = this.datasetData[0].fileName
    }
  }

  /**
   * 处理上传文件的数据 参数， 是否导入 脱敏 (use methods: handleSubmit )
   * @author Jinhui Hou
   */
  public dealWriteFileData(values: any) {
    const { haveSetDesensitization } = this
    const index = this.categoryOptions.indexOf(values.category)
    const fileType: string = this.fileList[0].name.slice(
      this.fileList[0].name.lastIndexOf('.') + 1
    )

    return {
      categoryId: DataStore.datasetCategory[index].categoryId,
      importType: fileType.includes('csv') ? 'csv' : 'excel',
      name: values.datasetName,
      files: this.datasetData.map((item: any) => {
        return {
          filename: item.fileName,
          firstLineAsFields: this.firstLineAsFields,
          identifier: this.fileMd5,
          data: item.newHead.map(
            (column: {
              name: string
              type: string
              semantic?: string
              recommendSemantic: string
            }) => {
              // 某张表 某列的配置
              const columnSet = haveSetDesensitization[item.name][column.name]
              return {
                name: column.name,
                type: column.type,
                dataLevelId: columnSet?.masking ? columnSet.dataLevelId : null,
                dataMaskingType: columnSet?.masking
                  ? columnSet.dataMaskingType
                  : null,
                importColumn: columnSet?.importColumn,
                semantic: column.semantic ? column.semantic : 'null',
              }
            }
          ),
          sheetName: item.name,
        }
      }),
      charSet: this.fileCodingType,
      separate: this.tableSeparate,
      quote: this.tableQuote,
      escape: this.tableEscape,
      firstImport: true,
    }
  }

  /**
   * 处理外部数据库导入 各表的配置
   * @author Jinhui Hou
   */
  public dealDatabaseData(tables: any) {
    const defaultTimingConfig = {
      beginDate: moment(new Date()),
      daysOfWeek: 'SUN',
      hourOfDay: 0,
      incrementColumn: '',
      minuteOfHour: 0,
      needSchedule: false,
      intervalOfHour: 1,
      scheduleType: 'day',
    }
    const { haveSetDesensitization, databaseHeadSemantic } = this
    const tableConfigs = tables.map((table: string) => {
      const tableColumns: any[] = []
      // 由于目前tables 是多张表的表名数组 ['table1'， 'table2'] ,
      // 列的配置 从 已经配置好的haveSetDesensitization 配置中遍历获取。
      Object.keys(haveSetDesensitization[table]).forEach((column: string) => {
        const currentColumn = haveSetDesensitization[table][column]
        const columnSemantic = databaseHeadSemantic[table][column]
        tableColumns.push({
          name: column,
          type: columnSemantic.type,
          semantic: columnSemantic.semantic || columnSemantic.recommendSemantic,
          dataLevelId: currentColumn.masking ? currentColumn.dataLevelId : null,
          dataMaskingType: currentColumn.masking
            ? currentColumn.dataMaskingType
            : null,
          importColumn: currentColumn.importColumn,
        })
      })
      const timeConfig = this.timingSettingMaps
        ? this.timingSettingMaps[table]
        : {}
      return {
        tableName: table,
        data: tableColumns,
        datasetScheduleConfig: this.timingSettingMaps
          ? timeConfig
          : defaultTimingConfig,
      }
    })
    return tableConfigs
  }

  /**
   * 点击 确定 button 提交数据
   */
  public handleSubmit(event: Event) {
    event.preventDefault()
    if (this.selectedDataType === 'table') {
      // 校验并获取一组输入域的值与 Error，若 fieldNames 参数为空，则校验全部组件
      this.form.validateFields(async (error: Error, values: any) => {
        if (!error) {
          // 处理后的 请求参数 加入了 列的是否导入 是否脱敏 等参数
          const passData = this.dealWriteFileData(values)
          // 解决空文件上传的问题
          if (
            !(
              passData.files &&
              passData.files[0] &&
              passData.files[0].data &&
              passData.files[0].data.length > 0
            )
          ) {
            this.$message.error('请确保上传文件包含有效数据')
            this.deleteFile()
            return
          }
          this.spinningMessage = '上传中'
          this.spinning = true
          try {
            const response = await DataStore.writeFinalDataset(passData)
            // 导入数据不成功的话
            if (response.data.code !== 100) {
              // (this.$refs.messageModal as MessageModal).setData(response.data.message,response.data.tips,true)
              DataStore.setMessageModalOption({
                visible: true,
                message: '导入数据失败',
                tips: `原因：${response.data.message}\n提示：${response.data.tips}`,
              })
              // 数据转化类型成功 处理提交失败
              if (response.data.code !== 40013 && this.fileType === 'csv') {
                this.handleSubmitFail(values)
              }
            }
            // 数据导入成功
            if (DataStore.writeDatasetSucceed) {
              DataStore.cancelImport()
              this.submitFailCount = 0
              this.$message.success('上传成功！')
              this.goBackPreView(
                this.selectedDataType,
                response.data.result.datasetId
              )
              this.$emit('set-selected-keys', [
                `dataset${response.data.result.datasetId}`,
              ])
              // data upload success, user guide to the next step
              this.updateDataSuccessToGuideStep(5)
            }
            this.spinning = false
            this.fileType = this.fileList[0].name.slice(
              this.fileList[0].name.lastIndexOf('.') + 1
            )
          } catch (error_) {
            console.error('writeFinalDataset error:', error_)
            // this.$message.error('上传失败!')
            this.spinning = false
          }
        } else {
          console.log('error:', error)
          this.fileType = ''
        }
      })
    } else if (this.selectedDataType === 'database') {
      this.form.validateFields(async (error: Error, values: any) => {
        if (
          !this.isDatabaseChosen ||
          DataStore.testState !== DatabaseTestState.TEST_SUCCEED
        ) {
          this.databaseConfigValidateMessage = '请先配置数据库并进行连接测试！'
          return
        }
        // if (this.databaseTables.length === 0) {
        //   this.databaseConfigValidateMessage =
        //     '该数据库没有数据表，请重新配置！'
        //   return
        // }
        if (!error) {
          const index = this.categoryOptions.indexOf(values.category)
          const id = DataStore.datasetCategory[index].categoryId
          const tableConfigs = this.dealDatabaseData(values.tables)
          const passData = {
            categoryId: id,
            ...DataStore.databaseConfig,
            importType: 'table',
            tableConfigs,
          }
          this.$emit('begin-database-upload', passData)
        } else {
          console.log('error:', error)
        }
      })
    } else if (this.selectedDataType === 'http') {
      // http 模式下创建表
      const { haveSetDesensitization } = this
      this.form.validateFields(async (error: Error, values: any) => {
        if (!error) {
          const index = this.categoryOptions.indexOf(values.category)
          const id = DataStore.datasetCategory[index].categoryId
          const dataSend = {
            categoryId: id,
            columnDTOList: this.datasetData[0].newHead.map(
              (column: {
                name: string
                type: string
                semantic?: string
                recommendSemantic: string
              }) => {
                const columnSet =
                  haveSetDesensitization[this.datasetData[0].fileName][
                    column.name
                  ]
                return {
                  name: column.name,
                  type: column.type,
                  dataLevelId: columnSet.masking ? columnSet.dataLevelId : null,
                  dataMaskingType: columnSet.masking
                    ? columnSet.dataMaskingType
                    : null,
                  importColumn: columnSet.importColumn,
                  semantic: column.semantic ? column.semantic : 'null',
                }
              }
            ),
            data: Object.values(this.datasetData[0].data[0]),
            importType: 'http',
            name: this.datasetData[0].fileName,
          }
          // 保存http表信息，这里用回调方式方便监听错误信息
          HttpCreateTable({
            data: dataSend,
          }).then((response) => {
            if (response.data.code !== 100) {
              DataStore.setMessageModalOption({
                visible: true,
                message: '导入数据失败',
                tips: `原因：${response.data.message}\n提示：${response.data.tips}`,
              })
            }
            if (response.data.code === 100) {
              DataStore.cancelImport()
              this.$message.success('操作成功！')
              this.goBackPreView(this.selectedDataType, response.data.result)
              DataStore.updateHttpDataSet(response.data.result)
                .then(() => {
                  this.$emit('set-selected-keys', [
                    `dataset${response.data.result}`,
                  ])
                })
                .catch(() => {
                  this.$message.error('获取表信息失败，请刷新重试')
                })
              // data upload success, user guide to the next step
              this.updateDataSuccessToGuideStep(5)
            }
          })
        }
      })
    } else if (this.selectedDataType === 'graph') {
      // 图数据
      this.form.validateFields(async (error: Error) => {
        if (!error) {
          // 处理后的 请求参数 加入了 列的是否导入 是否脱敏 等参数
          const passData = this.graphData
          // 解决空文件上传的问题
          // if(!(passData.file && passData.fileList[0] && passData.fileList[0].data && (passData.file[0].data.length > 0))) {
          //   this.$message.error('请确保上传文件包含有效数据')
          //   this.deleteFile()
          //   return
          // }
          this.spinningMessage = '上传中'
          this.spinning = true
          try {
            const response = await DataStore.writeGraphDataset(passData)
            if (response.data.code !== 100) {
              DataStore.setMessageModalOption({
                visible: true,
                message: '导入数据失败',
                tips: `原因：${response.data.message}\n提示：${response.data.tips}`,
              })
            }
            if (DataStore.writeDatasetSucceed) {
              this.goBackPreView(this.selectedDataType, 0)
              DataStore.cancelImport()
              this.$message.success('上传成功！')
              // data upload success, user guide to the next step
              this.updateDataSuccessToGuideStep(5)
            }
            this.$emit('set-selected-keys', [`graph-${response.data.result}`]) // 图文件要带graph-前缀
            this.spinning = false
          } catch (error_) {
            console.error('writeFinalDataset error:', error_)
            // this.$message.error('上传失败!')
            this.spinning = false
          }
        } else {
          console.log('error:', error)
        }
      })
    }
  }

  // 回到之前跳转到这个数据导入的页面，数据视图加载数据/图分析加载数据
  public async goBackPreView(selectedDataType: string, datasetId: number = -1) {
    let lastUrl: any = sessionStorage.getItem('lastUrl')
    if (lastUrl !== undefined) {
      lastUrl = JSON.parse(lastUrl)
      const { href } = this.$router.resolve({
        path: lastUrl.url,
      })

      // 如果是表格数据
      if (selectedDataType === 'table' || selectedDataType === 'http') {
        const { projectId, loadType } = lastUrl
        if (loadType === 'table') {
          const data = { projectId, datasetIds: [datasetId] }
          await importData({ data })
        }
      }
      sessionStorage.removeItem('lastUrl')
      window.location.href = href
    } else {
      this.$router.push('/management/data')
    }
  }

  /**
   * 提交失败信息
   * SubmitFailModal。vue $emit
   */
  public handleErrorFileSubmit() {
    DataStore.setSubmitFailModalVisible(false)
    this.fileType = ''
    if (this.selectedDataType === 'table') {
      // 表格型数据
      this.form.validateFields(async (error: Error, values: any) => {
        if (!error) {
          const passData = this.dealWriteFileData(values)
          passData.firstImport = false

          this.spinningMessage = '上传中'
          this.spinning = true
          try {
            const response = await DataStore.writeErrorFinalDataset(passData)

            // 导入数据不成功的话
            if (!response.data.result) {
              DataStore.setMessageModalOption({
                visible: true,
                message: '导入数据失败',
                tips: `原因：${response.data.message}\n提示：${response.data.tips}`,
              })
            }
            if (DataStore.writeDatasetSucceed) {
              DataStore.cancelImport()
              this.$message.success('上传成功！')
              if (!response.data.result.successed) {
                this.$message.info(
                  `导入${response.data.result.inserted}行，类型错误${response.data.result.formatErrors}行。`
                )
              }
              this.$router.push('/management/data')
            }
            this.$emit('set-selected-keys', [
              `dataset${response.data.result.datasetId}`,
            ])
            this.spinning = false
            this.fileType = this.fileList[0].name.slice(
              this.fileList[0].name.lastIndexOf('.') + 1
            )
          } catch (error_) {
            console.error('writeFinalDataset error:', error_)
            // this.$message.error('上传失败!')
            this.spinning = false
          }
        } else {
          console.log('error:', error)
          this.fileType = ''
        }
      })
    }
  }

  /**
   * 点击确定提交数据 出现错误的情况
   */
  public handleSubmitFail(values: any) {
    const postData = this.getErrorTableInfo(values)
    const errorResult: any = DataStore.parseErrorFile(postData)
    // 已上传文件信息修改
    this.fileType = ''
    // 呈现错误信息
    errorResult
      .then((result: any) => {
        if (
          result.data.result &&
          result.data.result.errorCount >= 0 &&
          result.data.result.errorLineCodeInfoMap
        ) {
          DataStore.setSubmitFailModalVisible(true)
          this.submitFailResult = result.data.result
        }
      })
      .catch((error: any) => {
        console.log('fail info error', error)
      })
    this.submitFailCount += 1
  }

  /**
   * 更改数据库配置
   * DataBaseModal。vue $emit
   */
  public setIsDatabaseChosen(status: any) {
    DataStore.setSelectedTables([])
    this.form.setFieldsValue({ tables: [] })
    this.isDatabaseChosen = status
    if (status === true) {
      this.databaseConfigValidateMessage = ''
    }
  }

  /**
   * 清空数据集名称input
   */
  public clearDatasetName($event?: any) {
    this.datasetNameClearStatus = false
    $event?.preventDefault()
    this.form.setFieldsValue({ datasetName: '' })
    this.datasetName = ''
    const target = this.$refs.datasetName
    target?.querySelectorAll('input')[0].focus()
  }

  public changeDatabaseName($event: any) {
    this.datasetName = $event.target.value
    this.datasetNameClearStatus = $event.target.value !== ''
  }

  // 清除mysql导入状态
  public clearImportStatus() {
    console.log('clear-database-import-')
  }

  /**
   * tab更换当前编辑的上传文件的文件名
   */
  public changeTable(fileName: string) {
    this.activeTableFileName = fileName
    this.getDataWithMask()
  }

  /**
   * 初始化数据库导入时的语义初始信息
   */
  public initDatabaseSemantic(tableHeadInfo: any[]) {
    const databaseHeadSemantic: any = {}
    tableHeadInfo.forEach((data: { tableName: string; head: any[] }) => {
      databaseHeadSemantic[data.tableName] = {}
      data.head.forEach((column: any) => {
        databaseHeadSemantic[data.tableName][column.name] = {
          recommendSemantic: column.recommendSemantic,
          type: column.type,
        }
      })
      if (data.tableName === this.selectedTableData.name) {
        // 给selectedTableData中塞语义
        const { head } = this.selectedTableData
        head.forEach((column: any) => {
          column.recommendSemantic = data.head.filter(
            (c) => c.name === column.name
          )[0].recommendSemantic
        })
        DataStore.setSelectedTableData({
          ...this.selectedTableData,
          head,
        })
      }
    })
    this.databaseHeadSemantic = databaseHeadSemantic
  }

  /**
   * 数据库导入方式切换语义
   */
  public onDatabaseChangeSemantic({
    tableName,
    colName,
    semantic,
  }: {
    tableName: string
    colName: string
    semantic: string
  }) {
    const data = this.databaseHeadSemantic[tableName] || {}
    data[colName].semantic = semantic
    this.databaseHeadSemantic[tableName] = data
    // 当修改的是当前选择的预览表
    if (this.selectedTableData.name === tableName) {
      const { head } = this.selectedTableData
      const index = head.findIndex((c: any) => c.name === colName)
      head.splice(index, 1, {
        ...head[index],
        semantic,
      })
      DataStore.setSelectedTableData({
        ...this.selectedTableData,
        head,
      })
    }
  }

  // 获取上传表格信息
  public getErrorTableInfo(value: any) {
    const categoryItem = DataStore.datasetCategory.find((item: any) => {
      return item.categoryName === value.category
    })

    const headData: any[] = []

    this.datasetData[0].head.forEach((head: any) => {
      const item = {
        name: head.name,
        type: head.type,
        semeantic: head.semantic === undefined ? 'null' : head.semantic,
        dataLevelId: null,
        dataMaskingType: null,
        importColumn: true,
      }
      headData.push(item)
    })

    const result: KeyValue = {
      categoryId: categoryItem?.categoryId,
      data: headData,
      filename: this.uploadDatasetData[0].fileName.split('_')[0],
      firstLineAsFields: this.firstLineAsFields,
      importType: this.fileList[0].name.split('.')[1],
      name: this.filename,
      identifier: this.fileMd5,
      sheetName: this.filename,
      charSet: this.fileCodingType,
      separate: this.tableSeparate,
      quote: this.tableQuote,
      escape: this.tableEscape,
    }

    return result
  }

  /**
   * 高级设置部分
   */
  // 分隔符号变换-选择
  public changeSeparate(value: string) {
    if (value !== '其他') {
      this.tableSeparate = value
    } else {
      this.tableSeparate = ''
    }
  }

  // 分隔符号变换-input
  public changeSeparateInput($event: any) {
    this.tableSeparate = $event.target.value
  }

  public changeQuote($event: any) {
    this.tableQuote = $event.target.value
  }

  public changeEscape($event: any) {
    this.tableEscape = $event.target.value
  }

  // 高级设置点击确定，重新上传
  public onAdvanceSettingChange() {
    this.codingTypeChanged(this.fileCodingType)
  }

  /**
   * 一键是否使用 推断数据类型
   */
  public changeTypeInfer(event: any) {
    this.isDataTypeInfer = event.target.checked
    this.editType(event.target.checked)
  }

  private updateTableHead(table: any, mark: boolean) {
    table.newHead.forEach((column: any, index: number) => {
      // 推断类型 checkbox 选中， 并且推断语义存在， 使用推断语义， 否则 varchar
      const type: string =
        mark && column.recommendType ? column.recommendType : 'varchar'
      column.type = type
      table.head.splice(index, 1, {
        ...table.head[index],
        type,
      })
    })
  }

  /**
   * 一次性修改所有列的数据类型
   * @param mark 是否使用推断的数据类型
   */
  public editType(mark: boolean) {
    // 表格文件 / http
    this.datasetData.forEach((table: any) => {
      this.updateTableHead(table, mark)
    })

    // 上传表格文件预览数据 修改 previewData
    if (this.selectedDataType === 'table') {
      this.previewData.forEach((table: any) => {
        this.updateTableHead(table, mark)
      })
    }
  }

  /**
   * @param event
   */
  public changeSemanticInfer(event: any) {
    this.isDataSemanticInfer = event.target.checked
    this.editSemantic(event.target.checked)
  }

  /**
   * 一次性修改所有列的语义
   * @param mark 是否使用推断的语义
   */
  public editSemantic(mark: boolean) {
    switch (this.selectedDataType) {
      // 数据库导入
      case 'database': {
        // 表
        Object.keys(this.databaseHeadSemantic).forEach((tableName: string) => {
          const currentTable: any = this.databaseHeadSemantic[tableName]
          // 列
          Object.keys(currentTable).forEach((colName: string) => {
            const column: any = currentTable[colName]
            const semantic: string =
              mark && column.recommendSemantic
                ? column.recommendSemantic
                : 'null'
            this.onDatabaseChangeSemantic({ tableName, colName, semantic })
          })
        })
        break
      }
      case 'http': {
        this.changeDatasetDataSemantic(mark)
        break
      }
      // 本地上传
      default: {
        this.changePreviewDataSemantic(mark)
        this.changeDatasetDataSemantic(mark)
        break
      }
    }
  }

  private changeSemanticDataTableHead(table: any, mark: boolean) {
    table.newHead.forEach((column: any, index: number) => {
      const semantic: string =
        mark && column.recommendSemantic ? column.recommendSemantic : 'null'
      column.semantic = semantic
      table.head.splice(index, 1, {
        ...table.head[index],
        semantic,
      })
    })
  }

  /**
   * 修改 previewData 是否使用推断语义
   * @param mark
   */
  public changePreviewDataSemantic(mark: boolean) {
    this.previewData.forEach((table: any) => {
      this.changeSemanticDataTableHead(table, mark)
    })
  }

  /**
   * 修改 datasetData 是否使用推断语义
   * @param mark
   */
  public changeDatasetDataSemantic(mark: boolean) {
    this.datasetData.forEach((table: any) => {
      this.changeSemanticDataTableHead(table, mark)
    })
  }

  /**
   * gis
   */
  public gisVisibleConfirmButton: boolean = false // gis 上传文件 显示确认按钮

  public gisModalEmit(value: boolean) {
    this.gisVisibleConfirmButton = value
  }

  /**
   * 更新 spinning loading
   */
  public updateSpinState({ value, message }: any) {
    this.spinning = value
    if (message) {
      this.spinningMessage = message
    }
  }

  /**
   * GIS table上传后预览的数据
   */
  public handleGisDataChange(data: any) {
    this.datasetData = (data || []).map((item: any) => {
      return {
        ...item,
        newHead: item.head,
      }
    })
  }

  /**
   * GisDataInfo 确认后 Emit
   * 更新数据列表， 并设置刚才上传的数据 datasetId 预览
   * @param datasetId
   */
  public async setSelectedKeysOfGis(datasetId: any) {
    const lastUrl: any = sessionStorage.getItem('lastUrl')
    if (lastUrl) {
      await this.goBackPreView('gis')
    } else {
      const gisModalCom: any = (this.$refs as any).gisModalCom as Vue
      gisModalCom.clearFormFileInfo()
      // 更新列表 并设置预览的数据
      const response: any = await DataStore.queryCurrentUserDatasetCategoryForGis(
        datasetId
      )
      // 这里主要是展开数据列表
      if (response.data.code === 100) {
        this.$emit('set-selected-keys', [`dataset${datasetId}`])
      }
    }
  }

  /**
   * 上传错误， 清空信息
   */
  public clearUpdateInfoOfGis() {
    const gisModalCom: any = (this.$refs as any).gisModalCom as Vue
    gisModalCom.clearFormFileInfo()
  }

  /**
   * 表头文字调整
   * @param tableIndex:number previewData中的第几个
   * @param
   */
  public handleTitleChange(
    tableIndex: number,
    {
      index: columnIndex,
      newValue,
      oldValue,
    }: { index: number; newValue: string; oldValue: string }
  ) {
    this.previewData[tableIndex].newHead[columnIndex].name = newValue
    this.datasetData[tableIndex].newHead[columnIndex].name = newValue
    // 处理返回来的数据
    this.previewData[tableIndex].data = this.previewData[tableIndex].data.map(
      (item: any) => {
        const newData = {
          ...item,
          [newValue]: item[oldValue],
        }
        delete newData[oldValue]
        return newData
      }
    )
    this.datasetData[tableIndex].data = this.datasetData[tableIndex].data.map(
      (item: any) => {
        const newData = {
          ...item,
          [newValue]: item[oldValue],
        }
        delete newData[oldValue]
        return newData
      }
    )

    // 处理脱敏配置
    const tableName = this.previewData[tableIndex].name
    const newData = {
      ...this.haveSetDesensitization[tableName],
      [newValue]: this.haveSetDesensitization[tableName][oldValue],
    }
    delete newData[oldValue]
    this.haveSetDesensitization = {
      ...this.haveSetDesensitization,
      [tableName]: newData,
    }
  }
}
</script>
<style lang="less" scoped>
.form-container {
  margin: 26px 24px 0;
  min-width: 800px;
}

/* tag 导入数据 */
.import-data-tag {
  color: #5d637e;
  font-size: 14px;
  font-weight: 600;
  height: 20px;
  letter-spacing: 1px;
  line-height: 20px;
  margin-bottom: 12px;
  padding-left: 12px;
  position: relative;

  span {
    color: #e74c3c;
    font-size: 14px;
    font-weight: 500;
  }

  &::before {
    background: #5d637e;
    content: '';
    height: 16px;
    left: 0;
    position: absolute;
    top: 2px;
    width: 4px;
  }
}

/* 导入数据类型 */
.import-row-first {
  margin-bottom: 6px;
  margin-left: 9px;
  width: 100%;

  .import-data-type {
    display: flex;
    width: 424px;
  }

  /deep/ .ant-form-item-label-left {
    width: 98px;
  }
}

/* 上传导入的文件 */
.import-row-two-file {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 6px;
  margin-left: 9px;

  // /deep/ button {
  //   left: -310px;
  //   position: absolute;
  //   top: -1px;
  // }

  .upload-file-select {
    height: 40px;
    width: 300px;
  }

  .upload-file-info {
    cursor: pointer;
    margin-left: 12px;
    min-width: 400px;
    top: 27px;

    .upload-file-text {
      color: #6973ff;
      display: inline-block;
      margin-left: 8px;
      margin-right: 8px;
      overflow: hidden;
      text-overflow: ellipsis;
      vertical-align: bottom;
      white-space: nowrap;
      // width: 100px;
    }
  }

  .progress-loading {
    align-items: center;
    display: flex;
    font-size: 12px;
    margin-left: 31px;
    width: 400px;

    .loading-text {
      margin-left: 10px;
    }
  }

  /deep/ .ant-form-item-label-left {
    width: 98px;
  }
}

/* 外部数据库导入 */
.import-row-two-database {
  align-items: center;
  display: flex;
  height: 52px;
  margin-bottom: 12px;
  margin-top: 12px;
  width: 100%;

  /deep/ .ant-form-item-label-left {
    height: 100%;
    line-height: 52px;
    width: 107px;
  }

  .database-type {
    width: 100%;

    /deep/ .select-box {
      height: 52px;
      margin-right: 16px;
      padding: 0;
      width: 80px;
    }
  }
}

/* 选择导入数据的列 */
.select-table-column {
  align-items: flex-start;
  display: flex;
  margin-bottom: 6px;
  width: 100%;

  .database-select-import {
    width: 600px;
  }
}

/* 数据分类， 数据集名称  设置第一行。。。 */
.import-row-general {
  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 6px;
  min-height: 38px;
  width: 100%;

  .row-three-column {
    flex-shrink: 0;
    height: 40px;
    width: 100%;
  }

  /* 将第一行作为数据表的列字段名称 */

  .set-first-row {
    align-items: center;
    display: flex;
  }

  /deep/ .ant-form-explain {
    left: 720px;
    position: absolute;
    top: 12px;
    width: 200px;
  }

  /deep/ .ant-form-item-label-left {
    width: 107px;
  }
}

/* 数据导入设置 */
.import-data-set {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  margin-left: 9px;

  .data-set-column {
    flex-shrink: 0;
    width: 400px;

    &.setting-row {
      .ant-row {
        display: inline-block;
        width: 50%;
      }
    }

    /* 脱敏设置 */

    .desensitization-set {
      align-items: center;
      display: flex;
      height: 40px;

      .data-setting-button {
        align-items: center;
        display: flex;

        .icon-font {
          margin-top: 4px;
        }
      }
    }
  }
}

.ant-form-model {
  margin-top: 46px;
}

.select-box,
.name-input {
  width: 150px;
}

// 上传选择框
.csv-upload button {
  font-size: 12px;
  height: 24px;
  line-height: 14px;
  padding-left: 12px;
  width: 97px;

  &:active,
  &:focus {
    border-color: rgb(217, 217, 217);
    color: rgba(0, 0, 0, 0.65);
  }
}

/deep/ .ant-upload-list {
  display: none;
}

.button-container {
  align-items: center;
  display: flex;
  flex-direction: row;
  justify-content: center;
  margin-bottom: 20px;

  button {
    margin-right: 10px;
  }

  .button {
    background-color: #6973ff;
  }
}

/* 单选框样式 */
.radio-container {
  display: block;
  height: 32px;
  line-height: 39px;
}

// 按钮、框的圆角样式
button,
/deep/ input,
/deep/ .ant-select-selection {
  border-radius: 4px;
}

.database-setting-button {
  margin-left: -15px;

  .setting-icon {
    color: #5561ff;
    margin-top: 4px;
  }

  &:hover {
    color: #5561ff;
  }
}

.no-table-text {
  color: #c6c6c6;
  margin-left: 37.5%;
}

// 任务进度展示相关
.task-progress-container {
  border-radius: 4px;
  left: 20px;
  position: absolute;
  right: 20px;
  top: 20px;
}

/deep/ .load-error-msg {
  color: #888;
  font-size: 14px;
  margin: 40px 0;
  text-align: center;

  .reload {
    color: #5561ff;
    cursor: pointer;
  }
}

/deep/ .ant-form-item-control {
  line-height: 36px;
}

.data-type-box {
  width: 260px;
}

.select-http-column {
  display: flex;
  flex-flow: row nowrap;
  height: 52px;
  margin-bottom: 12px;
  margin-top: 12px;
  width: 100%;

  /deep/ .ant-form-item-label {
    line-height: 32px !important;
  }

  /deep/ .ant-form-item-label-left {
    height: 100%;
    line-height: 52px !important;
    width: 107px;
  }

  /deep/ .select-box {
    height: 52px;
    margin-right: 16px;
    padding: 0;
    width: 80px;
  }

  .http-setting-button {
    margin-left: 10px;
  }
}

// 图数据预览
.data-preview-card {
  height: 450px;
  overflow: auto;

  pre {
    border: 1px solid #e9e9e9;
  }

  &::-webkit-scrollbar {
    height: 6px;
    width: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(144, 147, 153, 0.5);
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 5px;
  }
}

//折叠面板样式
/deep/ .ant-collapse {
  background: #fff;
  border: 0 solid #fff;

  .ant-collapse-item {
    border-bottom: 0 solid #fff;
    margin-bottom: 24px;
  }

  .ant-collapse-header {
    background: rgba(233, 233, 233, 0.2);
    border: 1px solid #e9e9e9;
    color: #373b52;
    font-size: 14px;
    font-weight: 600;
    height: 36px;
    letter-spacing: 1px;
    line-height: 36px;
    padding-bottom: 0;
    padding-top: 0;
  }

  .ant-collapse-content {
    border: 1px solid #e9e9e9;
    border-top: 0 solid #e9e9e9;

    .ant-collapse-content-box {
      overflow-x: auto;
      padding-left: 25px;
      width: 100%;

      &::-webkit-scrollbar {
        height: 6px;
        width: 6px;
      }

      &::-webkit-scrollbar-thumb {
        background: rgba(144, 147, 153, 0.5);
        border-radius: 6px;
      }

      &::-webkit-scrollbar-track {
        background: transparent;
        border-radius: 5px;
      }
    }
  }
}

// 按钮样式
/deep/ .import-data-type {
  margin-top: 5px;
  top: 0;

  button {
    color: #5d637e;
    font-size: 14px;
    font-weight: 400;
    height: 28px;
    letter-spacing: 1px;
    line-height: 28px;
    width: 106px;
  }

  .active-type-button {
    background: #6973ff;
    color: #fff;
  }
}

.import-data-type-prompt {
  background: rgba(0, 0, 0, 0.02);
  margin-left: 98px;
  margin-right: 21px;
  margin-top: 8px;
  min-width: 300px;
  padding: 8px 10px;
  width: calc(100% - 121px);

  div {
    color: rgba(0, 0, 0, 0.4);
    font-size: 12px;
    font-weight: 400;
    height: 17px;
    letter-spacing: 1px;
    line-height: 17px;
  }
}

.import-data-note-fold {
  background: rgba(0, 0, 0, 0.02);
  color: rgba(0, 0, 0, 0.4);
  cursor: pointer;
  font-size: 12px;
  font-weight: 500;
  height: 24px;
  line-height: 24px;
  margin-left: 98px;
  margin-right: 21px;
  text-align: center;
  width: calc(100% - 121px);
}

.icon-help {
  font-size: 20px;
  margin-left: 10px;
}

.import-row-general-required {
  margin-left: 0;
}

.import-row-general-norequired {
  /deep/ .ant-form-item-label-left {
    padding-left: 9px;
  }
}

.desensitization-set {
  /deep/ .ant-checkbox {
    display: inline-block;
  }
}

.active-database-type-button {
  border: 1px solid #6973ff;
  border-radius: 2px;
  box-shadow: 0 0 4px 0 #c4c8ff;
  height: 52px;
  margin-right: 16px;
  min-width: 80px;
}

.primary-database-type-button {
  height: 52px;
  margin-right: 16px;
  min-width: 80px;
}

.col-container {
  border-radius: 4px;
  margin-left: 0;
}

.upload-tips {
  color: #5d637e;
  font-size: 12px;
  padding-left: 11px;
}
</style>
